package com.bright.cwduijie.duijie.util.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.Cell;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import lombok.Getter;
import lombok.Setter;

import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.bright.cwduijie.common.util.DateUtil;

/**
 * @author hxj
 * @date 2024/1/20 12:00:00
 * @Description excel读取监听类
 */
public class ExcelReadListener implements ReadListener<Map<Integer, String>> {

    @Setter
    private List<ExcelReadFileItem> excelFileItems;

    @Getter
    private final List<Map<String, Object>> mapListData = new ArrayList<>();

    // 由外部传进来 应对多子表？
//    @Setter
//    private Map<String, List<Map<String, Object>>> sheetDataMap;

    // 标记当前excel是否已读取
    private boolean isOver = false;


//    private static final String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    @Override
    public void invoke(Map<Integer, String> integerStringMap, AnalysisContext analysisContext) {
        if (isOver) {
            // 已读取完毕，不继续读取
            return;
        }
        if (isMeetEnd(integerStringMap)) {
            isOver = true;
            return;
        }

        // 校验表头个数与实际Excel的总列数是否一致
        if (integerStringMap.size() != excelFileItems.size()) {
            throw new RuntimeException("表头字段个数不符！实为" + integerStringMap.size() + "，应为" + excelFileItems.size()
//                    + ": " + StringUtil.join(ListUtil.toSingleList(excelFileItems, ExcelReadFileItem::getFieldDoc), ",")
            );
        }

        Map<Integer, Cell> cellMap = analysisContext.readRowHolder().getCellMap();


        //获取一行数据中有多少null值
        long valueIsNullSize = integerStringMap.values().stream().filter(Objects::isNull).count();
        //如果是空行 则跳过
        if ((int) valueIsNullSize == (integerStringMap.size())) {
            return;
        }

        Map<String, Object> map = new LinkedHashMap<>();
        for (int i = 0; i < excelFileItems.size(); i++) {

            ExcelReadFileItem excelFileItem = excelFileItems.get(i);

            Object value;// = integerStringMap.get(i);

            //判断类型是否符合 根据类型做处理
            CellData cellData = (CellData) cellMap.get(i);

            //当前行当前列为空 则不进行其他处理
            if (cellData == null) {
                map.put(excelFileItems.get(i).getFieldName(), null);
                continue;
            }

            switch (excelFileItem.getFieldType()) {
                case CHAR:
                case MULTI_TEXT:
                    // 如果是数字类型
                    if (cellData.getType().equals(CellDataTypeEnum.NUMBER)) {
                        value = cellData.getNumberValue();
                    } else if (cellData.getType().equals(CellDataTypeEnum.STRING)) {
                        value = cellData.getStringValue();
                    } else if (cellData.getType().equals(CellDataTypeEnum.EMPTY)) {
                        value = "";
                    } else if (cellData.getType().equals(CellDataTypeEnum.ERROR)) {
                        value = "";
                    } else {
                        throw new RuntimeException(String.format("第%s行, 第%s列的'" + excelFileItem.getFieldDoc() + "'列的内容填写不符合,应为数值格式,请核实后重试"
                                , analysisContext.readRowHolder().getRowIndex() + 1, i));
                    }
                    break;

                case NUMBER:
                    //如果是数字类型
                    if (cellData.getType().equals(CellDataTypeEnum.NUMBER)) {
                        value = cellData.getNumberValue();
                    } else if (cellData.getType().equals(CellDataTypeEnum.STRING)) {
                        //需要尝试字符串能否转成一个数值
                        try {
                            Pattern pattern = Pattern.compile("^-?\\d+(\\.\\d+)?$");
                            Matcher isNum = pattern.matcher(cellData.getStringValue());
                            if (!isNum.matches()) {
                                throw new RuntimeException(String.format("第%s行, 第%s列的'" + excelFileItem.getFieldDoc() + "'列的内容填写不符合,应为数值格式,请核实后重试"
                                        , analysisContext.readRowHolder().getRowIndex() + 1, i));
                            } else {
                                value = Double.parseDouble(cellData.getStringValue());
                            }
                        } catch (Exception e) {
                            throw new RuntimeException(String.format("第%s行, 第%s列的'" + excelFileItem.getFieldDoc() + "'列的内容填写不符合,应为数值格式,请核实后重试"
                                    , analysisContext.readRowHolder().getRowIndex() + 1, i));
                        }
                    } else if (cellData.getType().equals(CellDataTypeEnum.BOOLEAN)) {
                        value = cellData.getBooleanValue().toString();
                    } else if (cellData.getType().equals(CellDataTypeEnum.EMPTY)) {
                        value = "";
                    } else if (cellData.getType().equals(CellDataTypeEnum.ERROR)) {
                        value = "";
                    } else {
                        throw new RuntimeException(String.format("第%s行, 第%s列的'" + excelFileItem.getFieldDoc() + "'列的内容填写不符合,应为数值格式,请核实后重试"
                                , analysisContext.readRowHolder().getRowIndex() + 1, i));
                    }
                    break;

                case DATE:
                    //如果是时间类型
                    if (cellData.getType().equals(CellDataTypeEnum.NUMBER)) {
                        BigDecimal excelDate = cellData.getNumberValue();
                        if (DateUtil.isValidExcelDate(excelDate.doubleValue())) {
                            Date javaDate = DateUtil.getJavaDate(excelDate.doubleValue());
                            value = DateUtil.formatDate(javaDate);
                        } else {
                            throw new RuntimeException(String.format("第%s行, 第%s列的'" + excelFileItem.getFieldDoc() + "'列的内容填写不符合,应为时间格式,请核实后重试"
                                    , analysisContext.readRowHolder().getRowIndex() + 1, i));
                        }
                    } else if (cellData.getType().equals(CellDataTypeEnum.STRING)) {
                        try {
                            Date date = DateUtil.parseDateString(cellData.getStringValue());
                            value = DateUtil.formatDate(date);
                        } catch (Exception e) {
                            throw new RuntimeException(String.format("第%s行, 第%s列的'" + excelFileItem.getFieldDoc() + "'列的内容填写不符合,应为时间格式,请核实后重试"
                                    , analysisContext.readRowHolder().getRowIndex() + 1, i));
                        }
                    } else if (cellData.getType().equals(CellDataTypeEnum.BOOLEAN)) {
                        value = cellData.getBooleanValue();
                    } else if (cellData.getType().equals(CellDataTypeEnum.EMPTY)) {
                        value = "";
                    } else if (cellData.getType().equals(CellDataTypeEnum.ERROR)) {
                        value = "";
                    } else {
                        throw new RuntimeException(String.format("第%s行, 第%s列的'" + excelFileItem.getFieldDoc() + "'错误的类型,请核实后重试"
                                , analysisContext.readRowHolder().getRowIndex() + 1, i));
                    }
                    break;
                default:
                    value = null;
                    break;
            }
            map.put(excelFileItems.get(i).getFieldName(), value);
        }
        mapListData.add(map);
    }

    /**
     * 结束条件 满足这个条件则后面的行不再读取
     * @param integerStringMap
     * @return
     */
    protected boolean isMeetEnd(Map<Integer, String> integerStringMap) {
        // 留给子类实现
        return false;
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
//        saveData();
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        // 如果没有传入表头对象excelFileItems 则根据表头
        if (excelFileItems == null) {
            System.out.println("未传入excelFileItems，根据excel表头生成，表头字段数为" + headMap.size());
            excelFileItems = new ArrayList<>(headMap.size());
            for (ReadCellData<?> readCellData : headMap.values()) {
                String headName = readCellData.getStringValue();
                excelFileItems.add(new ExcelReadFileItem(headName, headName, FieldType.CHAR));
            }
        }

        ReadListener.super.invokeHead(headMap, context);
    }

    /**
     * 出现异常回调
     *
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {

        // ExcelDataConvertException:当数据转换异常的时候，会抛出该异常，此处可以得知第几行，第几列的数据
        if (exception instanceof ExcelDataConvertException) {
            int columnIndex = ((ExcelDataConvertException) exception).getColumnIndex() + 1;
            int rowIndex = ((ExcelDataConvertException) exception).getRowIndex() + 1;
            String message = "第" + rowIndex + "行，第" + columnIndex + "列" + "数据格式有误，请核实";
            throw new RuntimeException(message);
        } else if (exception instanceof RuntimeException) {
            throw exception;
        } else {
            ReadListener.super.onException(exception, context);
        }
    }


}
